home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
MacHack 1997
/
MacHack 1997.toast
/
Hacks
/
Hacks ’93
/
Don's Hacks
/
AppBar Source
/
AppBarUnit.p
< prev
next >
Wrap
Text File
|
1993-06-18
|
20KB
|
712 lines
{
$Workfile: AppBarComp.p $
$Revision: 1.0 $
This handles all of our pallette stuff. Separated from AppBar.p for clarity.
© 1993 CE Software, Inc. All rights reserved.
WHEN WHO WHAT
•••••
•••••
}
UNIT AppBarUnit;
INTERFACE
USES Memtypes, Quickdraw, OSIntf, ToolIntf, PackIntf, MacPrint, GestaltEqu, Folders,
Errors, Notification, Processes, Aliases, TextServices, AppleEvents;
Function InitLayer:OSErr;
Procedure CloseLayer;
Procedure LayerIdleProc(var doneflag:boolean);
Function myGNEFilter (result:integer; var event:EventRecord):integer;
Procedure DoOpenApp(AllowUserInteraction:Boolean);
IMPLEMENTATION
{$D+} {$R-}
CONST
kClosePallette=-1;
kRecalcWindow=-2;
HiliteMode = $938; {[GLOBAL VAR] used for color highlighting}
MyItemWidth=22;
MyItemHeight=20;
MaxNumberIcons=20;
TYPE
MyAppInfo=record
name:str31;
TheType,TheCreator:OSType;
Thepsn:ProcessSerialNumber;
end;
MyGlobals=record
StoredPalletteWindow:windowptr; {MUST start our globals (TSMHelper assumes this}
MyProcess: ProcessSerialNumber;
WhatToDo: integer; {Message to do stuff}
PalletteIcons: integer;
BottomRight: point;
TheIcon: array[1..MaxNumberIcons] of MyAppInfo; {Names of icons in folder}
TempIcon: packed array[1..kSmall8BitIconSize] of char;
LastProcess: ProcessSerialNumber;
LastProcessIndex: integer;
end;
MyGlobalsPtr=^MyGlobals;
Function Storage:MyGlobalsPtr; external;
{This routine holds some data within our data, so we don't have to worry about A5}
Procedure HookUp; external;
{This connects our jGNE Event filter, necessary so that we can grab the events for our windoid}
Procedure UnHook; external;
{This unconnects our jGNE Event filter, so that we can quit cleanly.}
{--------------------------------------------------------------------------
Some color grapics utilities, to draw pretty stuff.
PlotColorPtr
A simple routine to draw a color icon.
PlotBWPtr
A simple routine to draw a black and white icon (not using any color QuickDraw calls)
ColorRectDepth
Gets the "depth" of the rectangle, to know whether to plot color or B&W
Not part of the layer stuff, used by our UpdateEvent handler.
--------------------------------------------------------------------------}
Procedure PlotColorPtr(wDepth,wFaceSize:integer; DestRect : Rect; p:ptr; themode:integer);
type bitmapPtr=^bitmap;
var hPixMap:PixMapHandle; pCurPort:grafptr;
begin
hPixMap:=NewPixMap;
MoveHHi(handle(hPixMap));
HLock(handle(hPixMap));
with hPixMap^^ do begin
hRes:=$480000;
vRes:=$480000;
pixelType:=0;
planeBytes:=0;
bounds.top:=0;
bounds.left:=0;
bounds.bottom:=wFaceSize;
bounds.right:=wFaceSize;
pixelSize:=wDepth;
cmpCount:=wDepth;
rowBytes:=((wDepth*wFaceSize) div 8)+$8000;
baseAddr:=pointer(p);
end;
DisposCTable(hPixMap^^.pmTable);
hPixMap^^.pmTable:=GetCTable(wDepth);
GetPort(pCurPort);
CopyBits(bitmapPtr(hPixMap^)^, pCurPort^.portbits, hPixMap^^.bounds,
DestRect,themode,nil);
hPixMap^^.baseaddr:=nil;
hunlock(handle(hPixMap));
disposPixMap(hPixMap);
end;
Procedure PlotBWPtr(wFaceSize:integer; DestRect : Rect; p:ptr);
var pCurPort:grafptr; mybitmap:bitmap;
begin
with mybitmap do begin
bounds.top:=0;
bounds.left:=0;
bounds.bottom:=wFaceSize;
bounds.right:=wFaceSize;
rowBytes:=wFaceSize div 8;
baseAddr:=pointer(p);
end;
GetPort(pCurPort);
CopyBits(mybitmap, pCurPort^.portbits, mybitmap.bounds,DestRect,0,nil);
end;
Function ColorRectDepth(therect:rect):integer;
var response: LONGINT; r2:rect; thegd:GDHandle;
begin
ColorRectDepth:=1;
if gestalt(gestaltQuickdrawVersion,response)=noerr then
if response>=gestalt8BitQD then begin {determine if we can use the color QD calls}
r2:=therect;
LocalToGlobal(r2.topleft);
LocalToGlobal(r2.botright);
thegd:=GetMaxDevice(r2);
if thegd<>nil then
ColorRectDepth:=thegd^^.gdPMap^^.pixelSize;
end;
end;
{--------------------------------------------------------------------------
FSDTManRefNum
Utility routine to get the refnum used by the DeskTop Manager
Not part of the layer stuff, used by our UpdateEvent handler.
--------------------------------------------------------------------------}
Function FSDTManRefNum(thevref:integer):integer;
var myDTPB: DTPBRec;
io:OSErr;
begin
myDTPB.ioNamePtr:=nil;
myDTPB.ioVRefNum:=thevref;
io:=PBDTGetPath(@myDTPB);
if io=NoErr then FSDTManRefNum:=myDTPB.ioDTRefnum
else FSDTManRefNum:=0;
end;
{--------------------------------------------------------------------------
PlotFileIcon
Gets appropriate icon for a file (from the DeskTop Manager) and plots it.
Not part of the layer stuff, used by our UpdateEvent handler.
--------------------------------------------------------------------------}
Procedure PlotFileIcon(which:integer);
var io:OSErr; flag:boolean; therect:rect;
Procedure HandleDTMan;
var myDTPB: DTPBRec;
io:OSErr;
vindex,tempref:integer;
mypb:paramBlockRec;
vname:str63;
Procedure TryBAndWDT;
begin
myDTPB.ioDTReqCount:=kSmallIconSize;
myDTPB.ioIconType:=kSmallIcon;
io:=PBDTGetIcon(@myDTPB,false);
if io=NoErr then begin
with storage^ do
PlotBWPtr(16,therect,@TempIcon);
exit(PlotFileIcon);
end;
end;
Procedure TryColorDT;
begin
myDTPB.ioDTReqCount:=kSmall8BitIconSize;
myDTPB.ioIconType:=kSmall8BitIcon;
io:=PBDTGetIcon(@myDTPB,false);
if io=NoErr then begin
with storage^ do
PlotColorPtr(8,16,therect,@TempIcon,0);
exit(PlotFileIcon);
end
else TryBAndWDT;
end;
begin
vindex:=1;
repeat
with mypb do begin
ioCompletion:=NIL;
ioNameptr:=@vname;
ioVRefNum:=0;
ioVolIndex:=vindex;
end;
io:=PBGetVInfo(@mypb,false);
if io=NoErr then begin
tempref:=FSDTManRefNum(mypb.iovrefnum);
if tempref<>0 then begin
with storage^,myDTPB do begin
ioDTRefNum:=tempref;
ioTagInfo:=0;
ioDTBuffer:=@TempIcon;
ioFileCreator:=storage^.TheIcon[which].TheCreator;
ioFileType:=storage^.TheIcon[which].TheType;
end;
if ColorRectDepth(therect)>2 then TryColorDT else TryBAndWDT;
end;
end;
vindex:=vindex+1;
until io<>NoErr;
end;
Procedure HandleSpecialIcon(whichicon:integer);
var h:handle;
Procedure TryBAndWSpecial;
begin
h:=GetResource('ics#',whichicon); {Get the icon to the folder out of the system file}
if h<>nil then begin
hlock(h);
PlotBWPtr(16,therect,pointer(h^));
hunlock(h);
end;
end;
Procedure TryColorSpecial;
begin
h:=GetResource('ics8',whichicon); {Get the icon to the folder out of the system file}
if h<>nil then begin
hlock(h);
PlotColorPtr(8,16,therect,pointer(h^),0);
hunlock(h);
end
else TryBAndWSpecial;
end;
begin
if ColorRectDepth(therect)>2 then TryColorSpecial else TryBAndWSpecial;
exit(PlotFileIcon);
end;
begin
therect:=storage^.StoredPalletteWindow^.portrect;
with therect do begin
left:=(which-1)*MyItemWidth;
right:=left+MyItemWidth;
eraserect(therect);
top:=(top+bottom-16) div 2;
bottom:=top+16;
left:=(left+right-16) div 2;
right:=left+16;
end;
{Maybe frame it}
with storage^ do io:=SameProcess(LastProcess,TheIcon[which].Thepsn,flag);
if flag then begin
insetrect(therect,-3,-3);
pensize(2,2);
framerect(therect);
pensize(1,1);
insetrect(therect,3,3);
end;
framerect(therect);
HandleDTMan;
if storage^.TheIcon[which].theType='APPL' then HandleSpecialIcon(-3996)
else HandleSpecialIcon(-4000)
end;
{--------------------------------------------------------------------------
CloseThePallette
If we've got a window, close it. Note just one call to close the service window.
This should only be called from the 'real' application, not from the event handler.
--------------------------------------------------------------------------}
Procedure CloseThePallette;
var io:OSErr;
begin
if storage^.StoredPalletteWindow<>nil then begin
io:=CloseServiceWindow(storage^.StoredPalletteWindow);
storage^.StoredPalletteWindow:=nil;
end;
end;
{--------------------------------------------------------------------------
DrawTheWindow
Just draw the window. Note that the ServiceWindow is just a
window for this purpose. Since we're calling this from our jGNE
Event Filter, we are NOT in our owner layer. So, we don't have our
A5 world, and our resource file isn't open. If this was a problem,
we could go through our IdleProc handler.
--------------------------------------------------------------------------}
Procedure DrawTheWindow;
var thewindow:windowptr;
index:integer;
begin
thewindow:=storage^.StoredPalletteWindow;
if thewindow<>nil then begin
beginupdate(thewindow);
setport(thewindow);
with storage^ do begin {Show all the icons}
for index:=1 to PalletteIcons do PlotFileIcon(index);
end;
endupdate(thewindow);
end;
end;
{--------------------------------------------------------------------------
OpenPalletteWindow
Creates the window. Count processes, create the window, remember the
processes.
--------------------------------------------------------------------------}
Function OpenPalletteWindow:OSErr;
var therect:rect;
thewindow:windowptr;
s2:str255;
index,thecount,NumIcons:integer;
myCPB:CInfoPBRec;
io:OSErr;
Procedure cio(io:OSErr);
begin
if io<>NoErr then begin
OpenPalletteWindow:=io;
exit(OpenPalletteWindow);
end;
end;
Procedure EnumerateProcesses;
var io,err:OSErr; psn:ProcessSerialNumber; myinfo:ProcessInfoRec; flag:boolean;
begin
io:=GetFrontProcess(storage^.LastProcess);
storage^.LastProcessIndex:=0;
psn.highLongOfPSN:=0;
psn.lowLongOfPSN:=kNoProcess;
thecount:=0;
repeat
io:=GetNextProcess(psn);
if io=NoErr then begin
myinfo.processName:=@s2;
myinfo.processInfoLength:=sizeof(myinfo);
myinfo.processAppSpec:=nil;
io:=GetProcessInformation(psn,myinfo);
if io=NoErr then
if bitand(myinfo.processMode,modeOnlyBackground)=0 then begin
thecount:=thecount+1;
with storage^.TheIcon[thecount] do begin
name:=s2;
TheType:=OSType(myinfo.processType);
TheCreator:=myinfo.processSignature;
Thepsn:=psn;
end;
io:=SameProcess(psn,storage^.LastProcess,flag);
if flag then storage^.LastProcessIndex:=thecount;
if thecount=MaxNumberIcons then exit(EnumerateProcesses);
end;
end;
until io<>NoErr;
end;
begin
OpenPalletteWindow:=NoErr;
EnumerateProcesses;
storage^.PalletteIcons:=thecount;
if storage^.StoredPalletteWindow<>nil then CloseThePallette;
with therect do begin
bottom:=storage^.BottomRight.v; right:=storage^.BottomRight.h;
if (storage^.PalletteIcons>0) then begin
top:=bottom-MyItemHeight;
left:=right-(storage^.PalletteIcons*MyItemWidth);
end
else begin
left:=right-150;
top:=bottom-1;
end;
end;
cio(NewServiceWindow(nil, therect,'AppBar',true,noGrowDocProc,pointer(-1),true,
componentInstance(kCurrentProcess),thewindow));
storage^.StoredPalletteWindow:=thewindow;
HiliteWindow(thewindow,true);
end;
{--------------------------------------------------------------------------
RecalcWindow
We've changed the front process! If both the new front and old front are in
my list, just redraw the icons. Otherwise, recreate the window.
--------------------------------------------------------------------------}
Procedure RecalcWindow;
var io:OSErr;
Function IsOK(which:integer):boolean;
var io,err:OSErr; myinfo:ProcessInfoRec;
begin
myinfo.processName:=nil;
myinfo.processInfoLength:=sizeof(myinfo);
myinfo.processAppSpec:=nil;
IsOK:=(GetProcessInformation(storage^.TheIcon[which].ThePsn,myinfo)=NoErr);
end;
Procedure FullRecalc;
var io:OSErr;
begin
io:=OpenPalletteWindow;
exit(RecalcWindow);
end;
Procedure CheckForNew;
var index:integer;
frontpsn:ProcessSerialNumber;
io:OSErr;
flag:boolean;
oldlast:integer;
tempport:grafptr;
begin
io:=GetFrontProcess(frontpsn);
with storage^ do begin
for index:=1 to PalletteIcons do begin
io:=SameProcess(frontpsn,TheIcon[index].ThePsn,flag);
if io=NoErr then
if flag then begin
oldlast:=LastProcessIndex;
LastProcessIndex:=index;
LastProcess:=frontpsn;
GetPort(tempport);
SetPort(storage^.StoredPalletteWindow);
PlotFileIcon(oldlast);
PlotFileIcon(index);
SetPort(tempport);
exit(RecalcWindow);
end;
end;
end;
FullRecalc;
end;
begin
SystemMenu($BF970002);
if not IsOK(storage^.LastProcessIndex) then FullRecalc
else CheckForNew;
end;
{--------------------------------------------------------------------------
SetWhatToDo
We don't want to do much from our jGNE Event Filter, because that lives
in other applications. So, we set a flag for what to do, and call
WakeUpProcess to break us out of our long WaitNextEvent.
--------------------------------------------------------------------------}
Procedure SetWhatToDo(thecommand:integer);
var io:OSErr;
begin
storage^.WhatToDo:=thecommand;
io:=WakeUpProcess(storage^.MyProcess);
end;
{--------------------------------------------------------------------------
LayerIdleProc
Called from our app's main event loop, if there's a flag for a deferred
action, we handle it now.
We've got a special handlers for quitting when the pallette is
closed.
If one of our icons have been clicked on, we'll switch to it it here. We
might have been able to do it from the frontmost app, but this is
safer.
--------------------------------------------------------------------------}
Procedure LayerIdleProc(var doneflag:boolean);
var io:OSErr;
begin
with storage^ do
case WhatToDo of
kClosePallette:doneFlag:=true; {Quit the AppBar}
kRecalcWindow:RecalcWindow;
otherwise
if WhatToDo>0 then
io:=SetFrontProcess(TheIcon[WhatToDo].Thepsn);
end;
storage^.WhatToDo:=0;
end;
{--------------------------------------------------------------------------
myGNEFilter
This is how we see the events for our window. It's called from assembler
glue to jGNEFilter (it's not a trap patching, though I'll admit it's a
technicallity).
This code executes in the heap and A5 world of whatever the frontmost app
is, it is NOT likely to be running inside of AppBar. So, globals should
be dealt with carefully if at all, and we need to be careful.
We only handle click events. We can hilite rectangles (but we MUST save and
restore the port, because it's not our port we're tromping over!) and use
normal window calls like TrackGoAway and DragWindow.
To detect our clicks, we call a different kind of FindWindow that finds a
service window. We then must make sure it's OUR service window (not all
service windows might be ours).
Once we've handled our events, we must make sure to turn them into null events,
or they will be passed on to the operating system!
We also check here to see if our window needs updating. Service Windows
appear not to get update events, so we have to check manually. (I could be
wrong on this, this is what I've discovered so far.) My update routine doesn't
use any resources of mine, which is fortunate, because again I'm not really in
my application's layer. If this was a problem, I could just flag that an updating
is necessary and handle it in my application's main event loop.
I also am checking the front process here to see if the system changed it on me, so
I need to update my window.
--------------------------------------------------------------------------}
Function myGNEFilter (result:integer; var event:EventRecord):integer;
var tempwindow:windowptr; typefind:integer;
Procedure CheckProcessChanged;
var psn:ProcessSerialNumber;
io:OSErr;
flag:boolean;
begin
io:=GetFrontProcess(psn);
io:=SameProcess(psn,storage^.LastProcess,flag);
if not flag then SetWhatToDo(kRecalcWindow);
end;
Procedure KillEvent;
begin
event.what:=nullEvent;
end;
Procedure HandleDrag;
var tempport:Grafptr;
dragrect:rect;
begin
Getport(tempport);
Setport(tempwindow);
SetRect(dragRect,-32767,-32767,32767,32767);
DragWindow(tempWindow,event.where,dragRect);
with storage^.BottomRight do begin
h:=tempwindow^.portrect.right;
v:=tempwindow^.portrect.bottom;
end;
LocalToGlobal(storage^.BottomRight);
Setport(tempport);
KillEvent;
end;
Procedure HandleClick;
var tempport:Grafptr;
ThePoint:point;
AmIn:Boolean;
TheRect:rect;
WhichItem:integer;
begin
KillEvent;
Getport(tempport);
Setport(tempwindow);
with storage^ do begin
ThePoint:=event.where;
GlobalToLocal(ThePoint);
WhichItem:=(ThePoint.h-tempwindow^.portrect.left) div MyItemWidth+1;
if (WhichItem>0) and (WhichItem<=PalletteIcons) then begin
TheRect:=tempWindow^.portrect;
with TheRect do begin
left:=(WhichItem-1)*MyItemWidth;
right:=left+MyItemWidth;
end;
BitClr(pointer(HILITEMODE),0);
InvertRect(TheRect);
AmIn:=true;
While WaitMouseUp do begin
GetMouse(ThePoint);
if PtInRect(ThePoint,TheRect)<>AmIn then begin
AmIn:=not AmIn;
BitClr(pointer(HILITEMODE),0);
InvertRect(TheRect);
end;
end;
if AmIn then begin
BitClr(pointer(HILITEMODE),0);
InvertRect(TheRect);
SetWhatToDo(WhichItem);
end;
end;
end;
SetPort(tempport);
end;
begin
if storage^.StoredPalletteWindow<>nil then
case event.what of
mouseDown:begin
typefind:=FindServiceWindow(event.where,tempWindow);
if typefind>inSysWindow then
if tempwindow=storage^.StoredPalletteWindow then begin
case typefind of
inDrag: HandleDrag;
inGoAway: begin
IF TrackGoAway(tempWindow,event.where) THEN
SetWhatToDo(kClosePallette);
KillEvent;
end;
inContent: HandleClick;
END;
end;
end;
nullEvent: begin
tempwindow:=storage^.StoredPalletteWindow;
if tempwindow<>nil then
if not EmptyRgn(windowpeek(tempwindow)^.updateRgn) then DrawTheWindow;
CheckProcessChanged;
end;
END; { of event case }
myGNEFilter:=result;
end;
{--------------------------------------------------------------------------
CloseLayer
This is called right before quitting. We MUST close the pallette, if
we don't, it'll stay around forever and be very annoying. We also have
to unhook ourselves from jGNEFilter. Yes, we could encounter problems if
someone has hooked jGNEFilter after we did. I don't have a solution for
that.
--------------------------------------------------------------------------}
Procedure CloseLayer;
begin
CloseThePallette;
UnHook;
end;
{--------------------------------------------------------------------------
InitLayer
This is called from our setup code to draw the window. We check to
make sure that we have the Text Services Manager here, and remember our
process serial number (so we can call WakeUpProcesson ourself). Finally,
we hook ourselves into jGNEFilter so that we can get the events for our
pallette.
--------------------------------------------------------------------------}
Function InitLayer:OSErr;
var io:OSErr;
theresult:longint;
begin
storage^.WhatToDo:=0;
InitLayer:=-1;
if gestalt(gestaltTSMgrVersion,theresult)<>NoErr then exit(InitLayer);
io:=GetCurrentProcess(storage^.MyProcess);
with storage^.BottomRight do begin
v:=screenbits.bounds.bottom-10;
h:=screenbits.bounds.right-40;
end;
if io=NoErr then HookUp;
InitLayer:=io;
end;
{--------------------------------------------------------------------------
DoOpenApp;
In Response to an AppleEvent, open the prefs folder. If no such
luck, select the folder.
--------------------------------------------------------------------------}
Procedure DoOpenApp(AllowUserInteraction:Boolean);
var io:OSErr;
begin
{Try to open our Prefs folder}
io:=OpenPalletteWindow;
end;
END.